package com.beiding.my.common.query;

import com.beiding.my.common.Bo;
import com.beiding.my.common.QuoteExpressionRegistry;
import com.google.common.base.Joiner;

import java.util.*;

public abstract class Expression {

    protected abstract String createExpression(QuoteExpressionRegistry registry, Class<? extends Bo> type, ParamHolderCreator paramHolderCreator, CreateResult createResult);

    public CreateResult createExpression(QuoteExpressionRegistry registry, Class<? extends Bo> type) {
        CreateResult createResult = new CreateResult();
        createResult.expression = this.createExpression(registry, type, new ParamHolderCreator(), createResult);
        if (createResult.quoteConditions.size() > 0) {
            createResult.expression = "(" + createResult.expression + ") and (" + Joiner.on(" and ").join(createResult.quoteConditions) + ")";
        }
        return createResult;
    }

    protected static class ParamHolderCreator {
        private int id = 1;
        private ParamHolderCreator() {
        }
        String create() {
            return "p" + (id++);
        }
    }


    public static class CreateResult {
        Map<String, Object> params = new HashMap<>();
        Set<String> quoteConditions = new HashSet<>();
        Set<Class> beTypes = new HashSet<>();
        public Set<Class> getBeTypes() {
            return beTypes;
        }
        private String expression;
        public Map<String, Object> getParams() {
            return params;
        }
        public String getExpression() {
            return expression;
        }
    }


    /****通用双目操作****/

    public InnerCompare eq(String field, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("=");
        binaryCompare.setField(field);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare ge(String field, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol(">=");
        binaryCompare.setField(field);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare gt(String field, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol(">");
        binaryCompare.setField(field);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare ne(String field, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("!=");
        binaryCompare.setField(field);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare lt(String field, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("<");
        binaryCompare.setField(field);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare le(String field, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("<=");
        binaryCompare.setField(field);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare like(String field, Object value) {
        if (value == null) {
            return isNull(field);
        }
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("like");
        binaryCompare.setField(field);
        binaryCompare.setValue("%" + value + "%");
        return binaryCompare;

    }

    public InnerCompare likeLeft(String field, Object value) {
        if (value == null) {
            return isNull(field);
        }
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("like");
        binaryCompare.setField(field);
        binaryCompare.setValue("%" + value);
        return binaryCompare;
    }

    public InnerCompare likeRight(String field, Object value) {
        if (value == null) {
            return isNull(field);
        }
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("like");
        binaryCompare.setField(field);
        binaryCompare.setValue(value + "%");
        return binaryCompare;
    }


    public InnerCompare notLike(String field, Object value) {
        if (value == null) {
            return isNull(field);
        }
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("not like");
        binaryCompare.setField(field);
        binaryCompare.setValue("%" + value + "%");
        return binaryCompare;
    }

    /*in操作*/
    public InnerCompare in(String field, Object... fs) {
        return in(field, Arrays.asList(fs));
    }

    public InnerCompare notIn(String field, Object... fs) {
        return notIn(field, Arrays.asList(fs));
    }

    public InnerCompare in(String field, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("in");
        binaryCompare.setField(field);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare notIn(String field, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("in");
        binaryCompare.setField(field);
        binaryCompare.setValue(value);
        return binaryCompare;
    }


    /****特殊操作****/
    public InnerCompare between(String field, Object from, Object to) {
        InnerCompareBetween between = new InnerCompareBetween(this);
        between.setSymbol("between");
        between.setField(field);
        between.setFrom(from);
        between.setTo(to);
        return between;
    }


    public InnerCompare notBetween(String field, Object from, Object to) {
        InnerCompareBetween between = new InnerCompareBetween(this);
        between.setSymbol("not between");
        between.setField(field);
        between.setFrom(from);
        between.setTo(to);
        return between;
    }

    public InnerCompare isNull(String field) {
        InnerCompareUnary unary = new InnerCompareUnary(this);
        unary.setSymbol("is null");
        unary.setField(field);
        return unary;
    }

    public InnerCompare isNotNull(String field, Object value) {
        InnerCompareUnary unary = new InnerCompareUnary(this);
        unary.setSymbol("not null");
        unary.setField(field);
        return unary;
    }

    public InnerCompare eq(FieldSelf fieldSelf, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("=");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare ge(FieldSelf fieldSelf, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol(">=");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare gt(FieldSelf fieldSelf, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol(">");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare ne(FieldSelf fieldSelf, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("!=");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare lt(FieldSelf fieldSelf, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("<");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare le(FieldSelf fieldSelf, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("<=");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare like(FieldSelf fieldSelf, Object value) {
        if (value == null) {
            return isNull(fieldSelf._selfName);
        }
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("like");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue("%" + value + "%");
        return binaryCompare;

    }

    public InnerCompare likeLeft(FieldSelf fieldSelf, Object value) {
        if (value == null) {
            return isNull(fieldSelf._selfName);
        }
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("like");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue("%" + value);
        return binaryCompare;
    }

    public InnerCompare likeRight(FieldSelf fieldSelf, Object value) {
        if (value == null) {
            return isNull(fieldSelf._selfName);
        }
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("like");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value + "%");
        return binaryCompare;
    }


    public InnerCompare notLike(FieldSelf fieldSelf, Object value) {
        if (value == null) {
            return isNull(fieldSelf._selfName);
        }
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("not like");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue("%" + value + "%");
        return binaryCompare;
    }

    /*in操作*/
    public InnerCompare in(FieldSelf fieldSelf, Object... fs) {
        return in(fieldSelf, Arrays.asList(fs));
    }

    public InnerCompare notIn(FieldSelf fieldSelf, Object... fs) {
        return notIn(fieldSelf, Arrays.asList(fs));
    }

    public InnerCompare in(FieldSelf fieldSelf, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("in");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value);
        return binaryCompare;
    }

    public InnerCompare notIn(FieldSelf fieldSelf, Object value) {
        InnerCompareBinary binaryCompare = new InnerCompareBinary(this);
        binaryCompare.setSymbol("in");
        binaryCompare.setField(fieldSelf._selfName);
        binaryCompare.setValue(value);
        return binaryCompare;
    }


    /****特殊操作****/
    public InnerCompare between(FieldSelf fieldSelf, Object from, Object to) {
        InnerCompareBetween between = new InnerCompareBetween(this);
        between.setSymbol("between");
        between.setField(fieldSelf._selfName);
        between.setFrom(from);
        between.setTo(to);
        return between;
    }


    public InnerCompare notBetween(FieldSelf fieldSelf, Object from, Object to) {
        InnerCompareBetween between = new InnerCompareBetween(this);
        between.setSymbol("not between");
        between.setField(fieldSelf._selfName);
        between.setFrom(from);
        between.setTo(to);
        return between;
    }

    public InnerCompare isNull(FieldSelf fieldSelf) {
        InnerCompareUnary unary = new InnerCompareUnary(this);
        unary.setSymbol("is null");
        unary.setField(fieldSelf._selfName);
        return unary;
    }

    public InnerCompare isNotNull(FieldSelf fieldSelf, Object value) {
        InnerCompareUnary unary = new InnerCompareUnary(this);
        unary.setSymbol("not null");
        unary.setField(fieldSelf._selfName);
        return unary;
    }


}
